 /*	
 *				
 *					ISNW ---- It is a war.It is not warcraft!
 *
 *
 *					Author:					zhoukeli(haxixi_keli)
 *					Filename:				ISceneNode.h
 *					CreateTime:				2008/12/26
 *					ModifyTime:				2008/12/26
 *
 */

#ifndef __ISNW_I_SCENE_NODE_H__
#define __ISNW_I_SCENE_NODE_H__

#include "IReferenceCounted.h"
#include "isnwList.h"
#include "vector3d.h"
#include "isnwString.h"
#include "ISceneNodeAnimator.h"
#include "ITriangleSelector.h"
#include "matrix4.h"
#include "ISceneManager.h"
#include "aabbox3d.h"

namespace ISNW
{
namespace scene
{
	class ISceneManager;

	class ISceneNode : public IReferenceCounted
	{
	public:
		//! it must have a render
		virtual void render() = 0;

	public:
		//! constructor
		//! NEED: 
		//! 0--> scene manager, it must has a scene manager
		//! 1--> parent,0 if do not have one
		//! 2--> id,-1 if not given
		//! 3--> visible,false by default
		//! 4--> position,(0,0,0) by default
		//! 5--> rotation,(0,0,0) by default
		//! 6--> scale,(1,1,1) by default
		//! 7--> triangle selector, 0 if not given
		ISceneNode(ISceneManager* sm,	
				   ISceneNode* parent = 0,
				   s32 id = -1,
				   bool visible = false,
				   core::vector3df pos = core::vector3df(0,0,0),
				   core::vector3df rotate = core::vector3df(0,0,0),
				   core::vector3df scale = core::vector3df(1,1,1),
				   ITriangleSelector* triSelector = 0
				  )
			:scenemanager(sm),Parent(parent),ID(id),isVisible(visible),RelativePosition(pos),RelativeRotation(rotate),RelativeScale(scale)
			,TriangleSelector(triSelector)
		{
			// if it has a triangle selector, add its reference
			if(triSelector) triSelector->grab();

			if(parent) parent->addChild(this);

			updateAbsolutePosition();
		}

		//! destructor
		virtual ~ISceneNode()
		{
			// remove all children
			{
				core::list<ISceneNode*>::iterator iter(Children.begin());
				for(;iter != Children.end();++iter)
				{
					(*iter)->Parent = 0;
					(*iter)->drop();
				}

				Children.clear();

			} // end remove all children

			// remove all animators
			{
				core::list<ISceneNodeAnimator*>::iterator iter(Animators.begin());
				for(;iter != Animators.end();++iter)
				{
					(*iter)->drop();
				}

			} // end remove all animators

			// remove triangle selector
			{
				if(TriangleSelector)
				{
					TriangleSelector->drop();
				}
			}
		}

	public:
		virtual void setname(c8* n)
		{
			name = n;
		}

		virtual c8* getname() const
		{
			return name.c_str();
		}

		virtual u32 getMaterialCount() const
		{
			return 0;
		}

		virtual video::material& getMaterial(u32 idx)
		{
			return *((video::material*)0);
		}

		virtual void setMaterial(u32 stage,video::material)
		{
		}

		virtual void setMaterialTexture(u32 stage,video::ITexture* texture)
		{
			for(u32 i = 0; i < getMaterialCount(); ++i)
			{
				getMaterial(i).setTexture(stage,texture);
			}
		}

		virtual ISceneNode* getParent() const
		{
			return Parent;
		}

		virtual ITriangleSelector* getTriangleSelector() const
		{
			return TriangleSelector;
		}

		virtual void setTriangleSelector(ITriangleSelector* triSelector)
		{
			if(triSelector == TriangleSelector)	return;

			if(TriangleSelector)
			{
				TriangleSelector->drop();
			}
	
			if(triSelector)
			{
				triSelector->grab();
				TriangleSelector = triSelector;
			}

		}

		virtual const core::list<ISceneNodeAnimator*>& getAnimators() const
		{
			return Animators;
		}

		virtual core::list<ISceneNodeAnimator*>& getAnimator()
		{
			return Animators;
		}

		virtual void addAnimator(ISceneNodeAnimator* animator)
		{
			if(animator)
			{
				Animators.push_back(animator);
				animator->grab();
			}
		}

		virtual void setID(s32 id)
		{
			ID = id;
		}

		virtual s32 getID() const
		{
			return ID;
		}

		virtual void addChild(ISceneNode* child)
		{
			if(child && child != this)
			{
				child->grab();
				child->removeFromParent();
				Children.push_back(child);
				child->Parent = this;
			}
		}

		virtual void setParent(ISceneNode* parent)
		{
			grab();

			removeFromParent();

			if(parent)
			{
				parent->addChild(this);
			}

			drop();
		}

		virtual void removeFromParent()
		{
			if(Parent)
			{
				Parent->removeChild(this);
			}
		}

		virtual bool removeChild(ISceneNode* child)
		{
			core::list<ISceneNode*>::iterator iter(Children.begin());
			for(;iter != Children.end();++iter)
			{
				if((*iter) == child)
				{
					(*iter)->Parent = 0;
					(*iter)->drop();
					Children.erase(iter);
					
					return true;
				}
			}

			return false;
		}

		core::list<ISceneNode*>& getChildren()
		{
			return Children;
		}

		const core::list<ISceneNode*>& getChildren() const
		{
			return Children;
		}

		virtual bool IsVisible() const
		{
			return isVisible;
		}

		virtual void setVisible(bool v)
		{
			isVisible = v;
		}

		virtual bool removeAnimator(ISceneNodeAnimator* animator)
		{
			core::list<ISceneNodeAnimator*>::iterator iter = Animators.begin();
			for(;iter != Animators.end();++iter)
			{
				if((*iter) == animator)
				{
					(*iter)->drop();
					Animators.erase(iter);
					
					return true;
				}
			}

			return false;
		}

		virtual void updateAbsolutePosition()
		{
			if(Parent)
			{
				AbsoluteTransformation = getRelativeTransformation() * Parent->getAbsoluteTransformtion();
			}
			else
			{
				AbsoluteTransformation = getRelativeTransformation();
			}
		}

		virtual void onRegisterSceneNode()
		{
			if(isVisible)
			{
				core::list<ISceneNode*>::iterator iter = Children.begin();
				for(;iter != Children.end(); ++iter)
				{
					(*iter)->onRegisterSceneNode();
				}
			}
		}

		virtual void onAnimate(u32 Time)
		{
			if(!isVisible) return;

			core::list<ISceneNodeAnimator*>::iterator it = Animators.begin();

			for(;it != Animators.end(); ++it)
			{
				(*it)->animateNode(this,Time);
			}

			updateAbsolutePosition();

			core::list<ISceneNode*>::iterator it2 = Children.begin();
			for(; it2 != Children.end(); ++it2)
			{
				(*it2)->onAnimate(Time);
			}
		}

		virtual core::aabbox3df getBoundBox() const
		{
			return core::aabbox3df();
		}

	public:
		core::matrix4f& getAbsoluteTransformtion()
		{
			return AbsoluteTransformation;
		}

		const core::matrix4f& getAbsoluteTransformtion() const
		{
			return AbsoluteTransformation;
		}

		core::matrix4f getRelativeTransformation() const
		{
			core::matrix4f scale,rotate,translate;
			scale.setscale(RelativeScale);
			rotate.setrotate(RelativeRotation);
			translate.setranslation(RelativePosition);

			return scale*rotate*translate;
		}

		core::vector3df getPosition() const
		{
			return RelativePosition;
		}

		void setPosition(core::vector3df pos)
		{
			RelativePosition = pos;
		}

		core::vector3df getAbsolutePosition()
		{
			return AbsoluteTransformation.getranslation();
		}

		core::vector3df getRotation() const
		{
			return RelativeRotation;
		}

		void setRotation(core::vector3df rotate)
		{
			RelativeRotation = rotate;
		}

		core::vector3df getScale() const
		{
			return RelativeScale;
		}

		void setScale(core::vector3df scale)
		{
			RelativeScale = scale;
		}

	protected:
		//! it may have a name
		core::string			name;

		//! relative position with parent
		core::vector3df			RelativePosition;

		//! relative rotation with parent
		core::vector3df			RelativeRotation;

		//! relative scale with parent
		core::vector3df			RelativeScale;

		//! parent
		ISceneNode*				Parent;

		//! Absolute transform
		core::matrix4f			AbsoluteTransformation;

		//! children
		core::list<ISceneNode*>	Children;

		//! animators
		core::list<ISceneNodeAnimator*> Animators;

		//! id
		s32						ID;

		//! is visible
		bool					isVisible;

		//! triangle selector
		ITriangleSelector*		TriangleSelector;

		//! sceneManager
		ISceneManager*			scenemanager;
	};
} // end namespace scene
} // end namespace ISNW

#endif